home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / sun-calendar-event-dialog.js < prev    next >
Text File  |  2008-03-03  |  89KB  |  2,426 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Sun Microsystems code.
  15.  *
  16.  * The Initial Developer of the Original Code is Sun Microsystems.
  17.  * Portions created by the Initial Developer are Copyright (C) 2006
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Michael Buettner <michael.buettner@sun.com>
  22.  *   Philipp Kewisch <mozilla@kewis.ch>
  23.  *   Martin Schroeder <mschroeder@mozilla.x-home.org>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. // the following variables are constructed if the jsContext this file
  40. // belongs to gets constructed. all those variables are meant to be accessed
  41. // from within this file only.
  42. var gStartTime = null;
  43. var gEndTime = null;
  44. var gItemDuration = null;
  45. var gStartTimezone = null;
  46. var gEndTimezone = null;
  47. var gIsReadOnly = false;
  48. var gUserID = null;
  49. var gOrganizerID = null;
  50. var gPrivacy = null;
  51. var gURL = null;
  52. var gPriority = 0;
  53. var gStatus = "NONE";
  54. var gDictCount = 0;
  55. var gPrefs = null;
  56. var gLastRepeatSelection = 0;
  57. var gIgnoreUpdate = false;
  58. var gShowTimeAs = null;
  59. var gIsSunbird = false;
  60.  
  61. // update menu items that rely on focus
  62. function goUpdateGlobalEditMenuItems() {
  63.     goUpdateCommand('cmd_undo');
  64.     goUpdateCommand('cmd_redo');
  65.     goUpdateCommand('cmd_cut');
  66.     goUpdateCommand('cmd_copy');
  67.     goUpdateCommand('cmd_paste');
  68.     goUpdateCommand('cmd_selectAll');
  69. }
  70.  
  71. // update menu items that rely on the current selection
  72. function goUpdateSelectEditMenuItems() {
  73.     goUpdateCommand('cmd_cut');
  74.     goUpdateCommand('cmd_copy');
  75.     goUpdateCommand('cmd_delete');
  76.     goUpdateCommand('cmd_selectAll');
  77. }
  78.  
  79. // update menu items that relate to undo/redo
  80. function goUpdateUndoEditMenuItems() {
  81.     goUpdateCommand('cmd_undo');
  82.     goUpdateCommand('cmd_redo');
  83. }
  84.  
  85. // update menu items that depend on clipboard contents
  86. function goUpdatePasteMenuItems() {
  87.     goUpdateCommand('cmd_paste');
  88. }
  89.  
  90. function onLoad() {
  91.     // first of all retrieve the array of
  92.     // arguments this window has been called with.
  93.     var args = window.arguments[0];
  94.  
  95.     // The calling entity provides us with an object that is responsible
  96.     // for recording details about the initiated modification. the 'finalize'
  97.     // property is our hook in order to receive a notification in case the
  98.     // operation needs to be terminated prematurely. This function will be
  99.     // called if the calling entity needs to immediately terminate the pending
  100.     // modification. In this case we serialize the item and close the window.
  101.     if (args.job) {
  102.         // keep this context...
  103.         var self = this;
  104.  
  105.         // store the 'finalize'-functor in the provided job-object.
  106.         args.job.finalize = function() {
  107.             // store any pending modifications...
  108.             self.onAccept();
  109.  
  110.             var item = window.calendarItem;
  111.  
  112.             // ...and close the window.
  113.             window.close();
  114.  
  115.             return item;
  116.         }
  117.     }
  118.  
  119.     window.fbWrapper = args.fbWrapper;
  120.  
  121.     // the most important attribute we expect from the
  122.     // arguments is the item we'll edit in the dialog.
  123.     var item = args.calendarEvent;
  124.  
  125.     // new items should have a non-empty title.
  126.     if (item.isMutable && (!item.title || item.title.length <= 0)) {
  127.         item.title = calGetString("sun-calendar-event-dialog",
  128.                                   isEvent(item) ? "newEvent" : "newTask");
  129.     }
  130.  
  131.     window.onAcceptCallback = args.onOk;
  132.  
  133.     // we store the item in the window to be able
  134.     // to access this from any location. please note
  135.     // that the item is either an occurrence [proxy]
  136.     // or the stand-alone item [single occurrence item].
  137.     window.calendarItem = item;
  138.  
  139.     // we store the array of attendees in the window.
  140.     // clone each existing attendee since we still suffer
  141.     // from the 'lost x-properties'-bug.
  142.     window.attendees = [];
  143.     var attendees = item.getAttendees({});
  144.     if (attendees && attendees.length) {
  145.         for each (var attendee in attendees) {
  146.             window.attendees.push(attendee.clone());
  147.         }
  148.     }
  149.  
  150.     // we store the organizer of the item in the window.
  151.     // TODO: we clone the object since foreign X-props get lost
  152.     // during the roundtrip. In order to detect whether or not
  153.     // the item has been changed by the dialog we clone the organizer
  154.     // in any case to get rid of the X-props.
  155.     window.organizer = item.organizer && item.organizer.clone();
  156.  
  157.     window.isOccurrence = (item != item.parentItem);
  158.  
  159.     // we store the recurrence info in the window so it
  160.     // can be accessed from any location. since the recurrence
  161.     // info is a property of the parent item we need to check
  162.     // whether or not this item is a proxy or a parent.
  163.     var parentItem = item;
  164.     if (parentItem.parentItem != parentItem) {
  165.         parentItem = parentItem.parentItem;
  166.     }
  167.     window.recurrenceInfo = parentItem.recurrenceInfo;
  168.  
  169.     const kSUNBIRD_ID = "{718e30fb-e89b-41dd-9da7-e25a45638b28}";
  170.     var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
  171.                   .getService(Components.interfaces.nsIXULAppInfo);
  172.  
  173.     if (appInfo.ID == kSUNBIRD_ID) {
  174.         gIsSunbird = true;
  175.     }
  176.  
  177.     document.getElementById("sun-calendar-event-dialog").getButton("accept")
  178.             .setAttribute("collapsed", "true");
  179.     document.getElementById("sun-calendar-event-dialog").getButton("cancel")
  180.             .setAttribute("collapsed", "true");
  181.     document.getElementById("sun-calendar-event-dialog").getButton("cancel")
  182.             .parentNode.setAttribute("collapsed", "true");
  183.  
  184.     var prefService = Components.classes["@mozilla.org/preferences-service;1"]
  185.                       .getService(Components.interfaces.nsIPrefService);
  186.  
  187.     gPrefs = prefService.getBranch(null);
  188.  
  189.     loadDialog(window.calendarItem);
  190.  
  191.     opener.setCursor("auto");
  192.  
  193.     document.getElementById("item-title").focus();
  194.     document.getElementById("item-title").select();
  195. }
  196.  
  197. function onAccept() {
  198.     dispose();
  199.     onCommandSave(true);
  200.     return true;
  201. }
  202.  
  203. function onCommandCancel() {
  204.     // find out if we should bring up the 'do you want to save?' question...
  205.     var newItem = saveItem();
  206.     var oldItem = window.calendarItem.clone();
  207.  
  208.     newItem.deleteProperty("DTSTAMP");
  209.     oldItem.deleteProperty("DTSTAMP");
  210.  
  211.     // we need to guide the description text through the text-field since
  212.     // newlines are getting converted which would indicate changes to the
  213.     // text.
  214.     setElementValue("item-description", oldItem.getProperty("DESCRIPTION"));
  215.     setItemProperty(oldItem,
  216.                     "DESCRIPTION",
  217.                     getElementValue("item-description"));
  218.     setElementValue("item-description", newItem.getProperty("DESCRIPTION"));
  219.  
  220.     // compare old and new version of this item. we ask the item for its
  221.     // representation as icalString in order to have some easily comparable
  222.     // form we can work with.
  223.     if (newItem.icalString == oldItem.icalString) {
  224.         return true;
  225.     }
  226.  
  227.     var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  228.                         .getService(Components.interfaces.nsIPromptService);
  229.  
  230.     var promptTitle = calGetString("calendar",
  231.                                    isEvent(window.calendarItem) ?
  232.                                       "askSaveTitleEvent" :
  233.                                       "askSaveTitleTask");
  234.     var promptMessage = calGetString("calendar",
  235.                                      isEvent(window.calendarItem) ?
  236.                                         "askSaveMessageEvent" :
  237.                                         "askSaveMessageTask");
  238.  
  239.     var flags = promptService.BUTTON_TITLE_SAVE *
  240.                 promptService.BUTTON_POS_0 +
  241.                 promptService.BUTTON_TITLE_CANCEL *
  242.                 promptService.BUTTON_POS_1 +
  243.                 promptService.BUTTON_TITLE_DONT_SAVE *
  244.                 promptService.BUTTON_POS_2;
  245.  
  246.     var choice = promptService.confirmEx(null,
  247.                                          promptTitle,
  248.                                          promptMessage,
  249.                                          flags,
  250.                                          null,
  251.                                          null,
  252.                                          null,
  253.                                          null,
  254.                                          {});
  255.     switch (choice) {
  256.         case 0: // Save
  257.             onCommandSave(true);
  258.             return true;
  259.         case 2: // Don't save
  260.             return true;
  261.         default: // Cancel
  262.             return false;
  263.     }
  264. }
  265.  
  266. function onCancel() {
  267.     var result = onCommandCancel();
  268.     if (result == true) {
  269.         dispose();
  270.     }
  271.     return result;
  272. }
  273.  
  274. function timezoneString(tz) {
  275.     var tzid = tz.tzid;
  276.     var prefix = getTimezoneService().tzidPrefix;
  277.     if (tzid.indexOf(prefix) == 0) {
  278.         tzid = tzid.substring(prefix.length);
  279.     }
  280.     return tzid;
  281. }
  282.  
  283. function loadDialog(item) {
  284.     setElementValue("item-title", item.title);
  285.     setElementValue("item-location", item.getProperty("LOCATION"));
  286.  
  287.     loadDateTime(item);
  288.  
  289.     // add calendars to the calendar menulist
  290.     var calendarList = document.getElementById("item-calendar");
  291.     var indexToSelect = appendCalendarItems(item, calendarList, window.arguments[0].calendar);
  292.     if (indexToSelect > -1) {
  293.         calendarList.selectedIndex = indexToSelect;
  294.     }
  295.  
  296.     // Categories
  297.     var categoryMenuList = document.getElementById("item-categories");
  298.     var indexToSelect = appendCategoryItems(item, categoryMenuList);
  299.  
  300.     categoryMenuList.selectedIndex = indexToSelect;
  301.  
  302.     // URL
  303.     gURL = item.getProperty("URL");
  304.     updateDocument();
  305.  
  306.     // Description
  307.     setElementValue("item-description", item.getProperty("DESCRIPTION"));
  308.  
  309.     // Status
  310.     if (isEvent(item)) {
  311.         gStatus = item.hasProperty("STATUS") ?
  312.             item.getProperty("STATUS") : "NONE";
  313.         updateStatus();
  314.     } else {
  315.         setElementValue("todo-status", item.getProperty("STATUS"));
  316.     }
  317.  
  318.     // Task completed date
  319.     if (item.completedDate) {
  320.         updateToDoStatus(item.status, item.completedDate.jsDate);
  321.     } else {
  322.         updateToDoStatus(item.status);
  323.     }
  324.  
  325.     // Task percent complete
  326.     if (isToDo(item)) {
  327.         var percentCompleteInteger = 0;
  328.         var percentCompleteProperty = item.getProperty("PERCENT-COMPLETE");
  329.         if (percentCompleteProperty != null) {
  330.             percentCompleteInteger = parseInt(percentCompleteProperty);
  331.         }
  332.         if (percentCompleteInteger < 0) {
  333.             percentCompleteInteger = 0;
  334.         } else if (percentCompleteInteger > 100) {
  335.             percentCompleteInteger = 100;
  336.         }
  337.         setElementValue("percent-complete-textbox", percentCompleteInteger);
  338.     }
  339.  
  340.     // Priority
  341.     gPriority = parseInt(item.priority);
  342.     updatePriority();
  343.  
  344.     // Privacy
  345.     gPrivacy = item.privacy;
  346.     updatePrivacy();
  347.  
  348.     // load repeat details
  349.     loadRepeat(item);
  350.  
  351.     // load reminder details
  352.     loadReminder(item);
  353.  
  354.     // hide rows based on if this is an event or todo
  355.     updateStyle();
  356.  
  357.     updateDateTime();
  358.  
  359.     updateCalendar();
  360.  
  361.     // figure out what the title of the dialog should be and set it
  362.     updateTitle();
  363.  
  364.     var sendInvitesCheckbox = document.getElementById("send-invitations-checkbox");
  365.     if (item.getProperty("X-MOZ-SEND-INVITATIONS") != null) {
  366.         sendInvitesCheckbox.checked = (item.getProperty("X-MOZ-SEND-INVITATIONS") == "TRUE");
  367.     } else {
  368.         sendInvitesCheckbox.checked = false;
  369.     }
  370.  
  371.     updateAttendees();
  372.     updateRepeat();
  373.     updateReminder();
  374.  
  375.     // How easy would it be to just call hasProperty(), but unfortunately
  376.     // this is currently flawed and doesn't give us the answer we're longing for.
  377.     // hasProperty() unconditionally forwards the request to the parent item
  378.     // if the property doesn't exist at the occurrence. That's why we need to
  379.     // use this somewhat awkward construct.
  380.     gShowTimeAs = (item.getUnproxiedProperty("TRANSP") != null) ?
  381.         item.getUnproxiedProperty("TRANSP") : null;
  382.     updateShowTimeAs();
  383. }
  384.  
  385. function loadDateTime(item) {
  386.     var kDefaultTimezone = calendarDefaultTimezone();
  387.     if (isEvent(item)) {
  388.         var startTime = item.startDate;
  389.         var endTime = item.endDate;
  390.         var duration = endTime.subtractDate(startTime);
  391.  
  392.         // Check if an all-day event has been passed in (to adapt endDate).
  393.         if (startTime.isDate) {
  394.             startTime = startTime.clone();
  395.             endTime = endTime.clone();
  396.  
  397.             endTime.day--;
  398.             duration.days--;
  399.         }
  400.  
  401.         // store the start/end-times as calIDateTime-objects
  402.         // converted to the default timezone. store the timezones
  403.         // separately.
  404.         gStartTimezone = startTime.timezone;
  405.         gEndTimezone = endTime.timezone;
  406.         gStartTime = startTime.getInTimezone(kDefaultTimezone);
  407.         gEndTime = endTime.getInTimezone(kDefaultTimezone);
  408.         gItemDuration = duration;
  409.     }
  410.  
  411.     if (isToDo(item)) {
  412.         var startTime = null;
  413.         var endTime = null;
  414.         var duration = null;
  415.  
  416.         var hasEntryDate = (item.entryDate != null);
  417.         if (hasEntryDate) {
  418.             startTime = item.entryDate;
  419.             gStartTimezone = startTime.timezone;
  420.             startTime = startTime.getInTimezone(kDefaultTimezone);
  421.         } else {
  422.             gStartTimezone = kDefaultTimezone;
  423.         }
  424.         var hasDueDate = (item.dueDate != null);
  425.         if (hasDueDate) {
  426.             endTime = item.dueDate;
  427.             gEndTimezone = endTime.timezone;
  428.             endTime = endTime.getInTimezone(kDefaultTimezone);
  429.         } else {
  430.             gEndTimezone = kDefaultTimezone;
  431.         }
  432.         if (hasEntryDate && hasDueDate) {
  433.             duration = endTime.subtractDate(startTime);
  434.         }
  435.  
  436.         gStartTime = startTime;
  437.         gEndTime = endTime;
  438.         gItemDuration = duration;
  439.     }
  440. }
  441.  
  442.  
  443. function dateTimeControls2State(aKeepDuration) {
  444.     if (gIgnoreUpdate) {
  445.         return;
  446.     }
  447.  
  448.     var startWidgetId;
  449.     var endWidgetId;
  450.     if (isEvent(window.calendarItem)) {
  451.         startWidgetId = "event-starttime";
  452.         endWidgetId = "event-endtime";
  453.     } else {
  454.         if (!getElementValue("todo-has-entrydate", "checked")) {
  455.             gItemDuration = null;
  456.         }
  457.         if (!getElementValue("todo-has-duedate", "checked")) {
  458.             gItemDuration = null;
  459.         }
  460.         startWidgetId = "todo-entrydate";
  461.         endWidgetId = "todo-duedate";
  462.     }
  463.  
  464.     var saveStartTime = gStartTime;
  465.     var saveEndTime = gEndTime;
  466.     var kDefaultTimezone = calendarDefaultTimezone();
  467.  
  468.     var menuItem = document.getElementById('options-timezone-menuitem');
  469.     if (gStartTime) {
  470.         // jsDate is always in OS timezone, thus we create a calIDateTime
  471.         // object from the jsDate representation and simply set the new
  472.         // timezone instead of converting.
  473.         gStartTime = jsDateToDateTime(
  474.             getElementValue(startWidgetId),
  475.             (menuItem.getAttribute('checked') == 'true') ? gStartTimezone : kDefaultTimezone);
  476.     }
  477.     
  478.     if (gEndTime) {
  479.         if (aKeepDuration) {
  480.             gEndTime = gStartTime.clone();
  481.             if (gItemDuration) {
  482.                 gEndTime.addDuration(gItemDuration);
  483.                 gEndTime = gEndTime.getInTimezone(gEndTimezone);
  484.             }
  485.         } else {
  486.             var timezone = gEndTimezone;
  487.             if (timezone.isUTC) {
  488.                 if (gStartTime && !compareObjects(gStartTimezone, gEndTimezone)) {
  489.                     timezone = gStartTimezone;
  490.                 }
  491.             }
  492.             gEndTime = jsDateToDateTime(
  493.                 getElementValue(endWidgetId),
  494.                 (menuItem.getAttribute('checked') == 'true') ? timezone : kDefaultTimezone);
  495.         }
  496.     }
  497.  
  498.     if (getElementValue("event-all-day", "checked")) {
  499.         gStartTime.isDate = true;
  500.     }
  501.  
  502.     // calculate the new duration of start/end-time.
  503.     // don't allow for negative durations.
  504.     var warning = false;
  505.     if (!aKeepDuration && gStartTime && gEndTime) {
  506.         if (gEndTime.compare(gStartTime) >= 0) {
  507.             gItemDuration = gEndTime.subtractDate(gStartTime);
  508.         } else {
  509.             gStartTime = saveStartTime;
  510.             gEndTime = saveEndTime;
  511.             warning = true;
  512.         }
  513.     }
  514.  
  515.     updateDateTime();
  516.     updateTimezone();
  517.  
  518.     if (warning) {
  519.         var callback = function func() {
  520.             var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  521.                                 .getService(Components.interfaces.nsIPromptService);
  522.             promptService.alert(
  523.                 null,
  524.                 document.title,
  525.                 calGetString("calendar", "warningNegativeDuration"));
  526.         }
  527.         setTimeout(callback, 1);
  528.     }
  529. }
  530.  
  531. function updateEntryDate() {
  532.     updateDateCheckboxes(
  533.         "todo-entrydate",
  534.         "todo-has-entrydate",
  535.         {
  536.             isValid: function() {
  537.                 return gStartTime != null;
  538.             },
  539.             setDateTime: function(dt) {
  540.                 gStartTime = dt;
  541.             }
  542.         });
  543. }
  544.  
  545. function updateDueDate() {
  546.     updateDateCheckboxes(
  547.         "todo-duedate",
  548.         "todo-has-duedate",
  549.         {
  550.             isValid: function() {
  551.                 return gEndTime != null;
  552.             },
  553.             setDateTime: function(dt) {
  554.                 gEndTime = dt;
  555.             }
  556.         });
  557. }
  558.  
  559. function updateDateCheckboxes(aDatePickerId, aCheckboxId, aDateTime) {
  560.     if (gIgnoreUpdate) {
  561.         return;
  562.     }
  563.  
  564.     if (!isToDo(window.calendarItem)) {
  565.         return;
  566.     }
  567.  
  568.     // force something to get set if there was nothing there before
  569.     setElementValue(aDatePickerId, getElementValue(aDatePickerId));
  570.  
  571.     // first of all disable the datetime picker if we don't have a date
  572.     var hasDate = getElementValue(aCheckboxId, "checked");
  573.     setElementValue(aDatePickerId, !hasDate, "disabled");
  574.  
  575.     // create a new datetime object if date is now checked for the first time
  576.     if (hasDate && !aDateTime.isValid()) {
  577.         var date = jsDateToDateTime(getElementValue(aDatePickerId), calendarDefaultTimezone());
  578.         aDateTime.setDateTime(date);
  579.     } else if (!hasDate && aDateTime.isValid()) {
  580.         aDateTime.setDateTime(null);
  581.     }
  582.  
  583.     // calculate the duration if possible
  584.     var hasEntryDate = getElementValue("todo-has-entrydate", "checked");
  585.     var hasDueDate = getElementValue("todo-has-duedate", "checked");
  586.     if (hasEntryDate && hasDueDate) {
  587.         var start = jsDateToDateTime(getElementValue("todo-entrydate"));
  588.         var end = jsDateToDateTime(getElementValue("todo-duedate"));
  589.         gItemDuration = end.subtractDate(start);
  590.     } else {
  591.         gItemDuration = null;
  592.     }
  593.  
  594.     updateDateTime();
  595.     updateTimezone();
  596. }
  597.  
  598. function loadRepeat(item) {
  599.     var recurrenceInfo = window.recurrenceInfo;
  600.     setElementValue("item-repeat", "none");
  601.     if (recurrenceInfo) {
  602.         setElementValue("item-repeat", "custom");
  603.         var ritems = recurrenceInfo.getRecurrenceItems({});
  604.         var rules = [];
  605.         var exceptions = [];
  606.         for each (var r in ritems) {
  607.             if (r.isNegative) {
  608.                 exceptions.push(r);
  609.             } else {
  610.                 rules.push(r);
  611.             }
  612.         }
  613.         if (rules.length == 1) {
  614.             var rule = rules[0];
  615.             if (rule instanceof Components.interfaces.calIRecurrenceRule) {
  616.                 switch (rule.type) {
  617.                     case 'DAILY':
  618.                         if (rule.interval == 1 && !rule.isFinite) {
  619.                             if (!checkRecurrenceRule(rule, ['BYSECOND',
  620.                                                             'BYMINUTE',
  621.                                                             'BYHOUR',
  622.                                                             'BYMONTHDAY',
  623.                                                             'BYYEARDAY',
  624.                                                             'BYWEEKNO',
  625.                                                             'BYMONTH',
  626.                                                             'BYSETPOS'])) {
  627.                                 var ruleComp = rule.getComponent("BYDAY",
  628.                                                                  {});
  629.                                 if (ruleComp.length > 0) {
  630.                                     if (ruleComp.length == 5) {
  631.                                         for (var i = 0; i < 5; i++) {
  632.                                             if (ruleComp[i] != i + 2) {
  633.                                                 break;
  634.                                             }
  635.                                         }
  636.                                         if (i==5) {
  637.                                             setElementValue("item-repeat",
  638.                                                             "every.weekday");
  639.                                         }
  640.                                     }
  641.                                 } else {
  642.                                     setElementValue("item-repeat", "daily");
  643.                                 }
  644.                             }
  645.                         }
  646.                         break;
  647.                     case 'WEEKLY':
  648.                         if (!checkRecurrenceRule(rule, ['BYSECOND',
  649.                                                         'BYMINUTE',
  650.                                                         'BYDAY',
  651.                                                         'BYHOUR',
  652.                                                         'BYMONTHDAY',
  653.                                                         'BYYEARDAY',
  654.                                                         'BYWEEKNO',
  655.                                                         'BYMONTH',
  656.                                                         'BYSETPOS'])) {
  657.                             if (!rule.isFinite && rule.interval == 1) {
  658.                                 setElementValue("item-repeat", "weekly");
  659.                             } else if (!rule.isFinite && rule.interval == 2) {
  660.                                 setElementValue("item-repeat", "bi.weekly");
  661.                             }
  662.                         }
  663.                         break;
  664.                     case 'MONTHLY':
  665.                         if (!checkRecurrenceRule(rule, ['BYSECOND',
  666.                                                         'BYMINUTE',
  667.                                                         'BYDAY',
  668.                                                         'BYHOUR',
  669.                                                         'BYMONTHDAY',
  670.                                                         'BYYEARDAY',
  671.                                                         'BYWEEKNO',
  672.                                                         'BYMONTH',
  673.                                                         'BYSETPOS'])) {
  674.                             if (!rule.isFinite && rule.interval == 1) {
  675.                                 setElementValue("item-repeat", "monthly");
  676.                             }
  677.                         }
  678.                         break;
  679.                     case 'YEARLY':
  680.                         if (!checkRecurrenceRule(rule, ['BYSECOND',
  681.                                                         'BYMINUTE',
  682.                                                         'BYDAY',
  683.                                                         'BYHOUR',
  684.                                                         'BYMONTHDAY',
  685.                                                         'BYYEARDAY',
  686.                                                         'BYWEEKNO',
  687.                                                         'BYMONTH',
  688.                                                         'BYSETPOS'])) {
  689.                             if (!rule.isFinite && rule.interval == 1) {
  690.                                 setElementValue("item-repeat", "yearly");
  691.                             }
  692.                         }
  693.                         break;
  694.                 }
  695.             }
  696.         }
  697.     }
  698.  
  699.     var repeatMenu = document.getElementById("item-repeat");
  700.     gLastRepeatSelection = repeatMenu.selectedIndex;
  701.  
  702.     if (item.parentItem != item) {
  703.         disableElement("item-repeat");
  704.     }
  705. }
  706.  
  707. function updateReminder() {
  708.     commonUpdateReminder();
  709.     updateAccept();
  710. }
  711.  
  712. function saveDialog(item) {
  713.     // Calendar
  714.     item.calendar = document.getElementById("item-calendar")
  715.                             .selectedItem.calendar;
  716.  
  717.     setItemProperty(item, "title", getElementValue("item-title"));
  718.     setItemProperty(item, "LOCATION", getElementValue("item-location"));
  719.  
  720.     saveDateTime(item);
  721.  
  722.     if (isToDo(item)) {
  723.         var percentCompleteInteger = 0;
  724.         if (getElementValue("percent-complete-textbox") != "") {
  725.             percentCompleteInteger =
  726.                 parseInt(getElementValue("percent-complete-textbox"));
  727.         }
  728.         if (percentCompleteInteger < 0) {
  729.             percentCompleteInteger = 0;
  730.         } else if (percentCompleteInteger > 100) {
  731.             percentCompleteInteger = 100;
  732.         }
  733.         setItemProperty(item, "PERCENT-COMPLETE", percentCompleteInteger);
  734.     }
  735.  
  736.     setCategory(item, "item-categories");
  737.  
  738.     // URL
  739.     setItemProperty(item, "URL", gURL, "attachments");
  740.  
  741.     // Description
  742.     setItemProperty(item, "DESCRIPTION", getElementValue("item-description"));
  743.  
  744.     // Event Status
  745.     if (isEvent(item)) {
  746.         if(gStatus && gStatus != "NONE") {
  747.             item.setProperty("STATUS", gStatus);
  748.         } else {
  749.             item.deleteProperty("STATUS");
  750.         }
  751.     } else {
  752.         var status = getElementValue("todo-status");
  753.         if (status != "COMPLETED") {
  754.             item.completedDate = null;
  755.         }
  756.         setItemProperty(item, "STATUS",   status);
  757.     }
  758.  
  759.     // set the "PRIORITY" property if a valid priority has been
  760.     // specified (any integer value except *null*) OR the item
  761.     // already specifies a priority. in any other case we don't
  762.     // need this property and can safely delete it. we need this special
  763.     // handling since the WCAP provider always includes the priority
  764.     // with value *null* and we don't detect changes to this item if
  765.     // we delete this property.
  766.     if (capSupported("priority") &&
  767.         (gPriority || item.hasProperty("PRIORITY"))) {
  768.         item.setProperty("PRIORITY", gPriority);
  769.     } else {
  770.         item.deleteProperty("PRIORITY");
  771.     }
  772.  
  773.     // Transparency
  774.     if (gShowTimeAs) {
  775.         item.setProperty("TRANSP", gShowTimeAs);
  776.     } else {
  777.         item.deleteProperty("TRANSP");
  778.     }
  779.  
  780.     // Privacy
  781.     setItemProperty(item, "CLASS", gPrivacy, "privacy");
  782.  
  783.     if (item.status == "COMPLETED" && isToDo(item)) {
  784.         var elementValue = getElementValue("completed-date-picker");
  785.         item.completedDate = jsDateToDateTime(elementValue);
  786.     }
  787.  
  788.     saveReminder(item);
  789. }
  790.  
  791. function saveDateTime(item) {
  792.     var kDefaultTimezone = calendarDefaultTimezone();
  793.     if (isEvent(item)) {
  794.         var startTime = gStartTime.getInTimezone(gStartTimezone);
  795.         var endTime = gEndTime.getInTimezone(gEndTimezone);
  796.         var isAllDay = getElementValue("event-all-day", "checked");
  797.         if (isAllDay) {
  798.             startTime = startTime.clone();
  799.             endTime = endTime.clone();
  800.             startTime.isDate = true;
  801.             endTime.isDate = true;
  802.             endTime.day += 1;
  803.         } else {
  804.             startTime = startTime.clone();
  805.             startTime.isDate = false;
  806.             endTime = endTime.clone();
  807.             endTime.isDate = false;
  808.         }
  809.         setItemProperty(item, "startDate", startTime);
  810.         setItemProperty(item, "endDate", endTime);
  811.     }
  812.     if (isToDo(item)) {
  813.         var startTime = gStartTime && gStartTime.getInTimezone(gStartTimezone);
  814.         var endTime = gEndTime && gEndTime.getInTimezone(gEndTimezone);
  815.         setItemProperty(item, "entryDate", startTime);
  816.         setItemProperty(item, "dueDate", endTime);
  817.     }
  818. }
  819.  
  820. function updateTitle() {
  821.     var title = "";
  822.     var isNew = window.calendarItem.isMutable;
  823.     if (isEvent(window.calendarItem)) {
  824.         if (isNew) {
  825.             title = calGetString("calendar", "newEventDialog");
  826.         } else {
  827.             title = calGetString("calendar", "editEventDialog");
  828.         }
  829.     } else if (isToDo(window.calendarItem)) {
  830.         if (isNew) {
  831.             title = calGetString("calendar", "newTaskDialog");
  832.         } else {
  833.             title = calGetString("calendar", "editTaskDialog");
  834.         }
  835.     }
  836.     title += ': ';
  837.     title += getElementValue("item-title");
  838.     document.title = title;
  839. }
  840.  
  841. function updateStyle() {
  842.     const kDialogStylesheet = "chrome://calendar/content/sun-calendar-event-dialog.css";
  843.  
  844.     for each (var stylesheet in document.styleSheets) {
  845.         if (stylesheet.href == kDialogStylesheet) {
  846.             if (gIsSunbird) {
  847.                 stylesheet.insertRule(".lightning-only { display: none; }", 0);
  848.             }
  849.             if (isEvent(window.calendarItem)) {
  850.                 stylesheet.insertRule(".todo-only { display: none; }", 0);
  851.             } else if (isToDo(window.calendarItem)) {
  852.                 stylesheet.insertRule(".event-only { display: none; }", 0);
  853.             }
  854.             return;
  855.         }
  856.     }
  857. }
  858.  
  859. function onPopupShowing(menuPopup) {
  860.     if (isToDo(window.calendarItem)) {
  861.         var nodes = menuPopup.childNodes;
  862.         for (var i = nodes.length - 1; i >= 0; --i) {
  863.             var node = nodes[i];
  864.             if (node.hasAttribute('class')) {
  865.                 if (node.getAttribute('class').split(' ').some(
  866.                     function (element) {
  867.                         return element.toLowerCase() == 'event-only';
  868.                     })) {
  869.                     menuPopup.removeChild(node);
  870.                 }
  871.             }
  872.         }
  873.     }
  874. }
  875.  
  876. function updateAccept() {
  877.     var enableAccept = true;
  878.  
  879.     var kDefaultTimezone = calendarDefaultTimezone();
  880.  
  881.     // don't allow for end dates to be before start dates
  882.     var startDate;
  883.     var endDate;
  884.     if (isEvent(window.calendarItem)) {
  885.         startDate = jsDateToDateTime(getElementValue("event-starttime"));
  886.         endDate = jsDateToDateTime(getElementValue("event-endtime"));
  887.  
  888.         var menuItem = document.getElementById('options-timezone-menuitem');
  889.         if (menuItem.getAttribute('checked') == 'true') {
  890.             var startTimezone = gStartTimezone;
  891.             var endTimezone = gEndTimezone;
  892.             if (endTimezone.isUTC) {
  893.                 if (!compareObjects(gStartTimezone, gEndTimezone)) {
  894.                     endTimezone = gStartTimezone;
  895.                 }
  896.             }
  897.  
  898.             startDate = startDate.getInTimezone(kDefaultTimezone);
  899.             endDate = endDate.getInTimezone(kDefaultTimezone);
  900.  
  901.             startDate.timezone = startTimezone;
  902.             endDate.timezone = endTimezone;
  903.         }
  904.  
  905.         startDate = startDate.getInTimezone(kDefaultTimezone);
  906.         endDate = endDate.getInTimezone(kDefaultTimezone);
  907.  
  908.         // For all-day events we are not interested in times and compare only
  909.         // dates.
  910.         if (getElementValue("event-all-day", "checked")) {
  911.             // jsDateToDateTime returnes the values in UTC. Depending on the
  912.             // local timezone and the values selected in datetimepicker the date
  913.             // in UTC might be shifted to the previous or next day.
  914.             // For example: The user (with local timezone GMT+05) selected
  915.             // Feb 10 2006 00:00:00. The corresponding value in UTC is
  916.             // Feb 09 2006 19:00:00. If we now set isDate to true we end up with
  917.             // a date of Feb 09 2006 instead of Feb 10 2006 resulting in errors
  918.             // during the following comparison.
  919.             // Calling getInTimezone() ensures that we use the same dates as
  920.             // displayed to the user in datetimepicker for comparison.
  921.             startDate.isDate = true;
  922.             endDate.isDate = true;
  923.         }
  924.     } else {
  925.         startDate = getElementValue("todo-has-entrydate", "checked") ?
  926.             jsDateToDateTime(getElementValue("todo-entrydate")) : null;
  927.         endDate = getElementValue("todo-has-duedate", "checked") ?
  928.             jsDateToDateTime(getElementValue("todo-duedate")) : null;
  929.     }
  930.  
  931.     if (endDate && startDate && endDate.compare(startDate) == -1) {
  932.         enableAccept = false;
  933.     }
  934.  
  935.     var accept = document.getElementById("cmd_accept");
  936.     if (enableAccept) {
  937.         accept.removeAttribute('disabled');
  938.     } else {
  939.         accept.setAttribute('disabled', 'true');
  940.     }
  941.  
  942.     return enableAccept;
  943. }
  944.  
  945. function onUpdateAllDay() {
  946.     if (!isEvent(window.calendarItem)) {
  947.         return;
  948.     }
  949.     var allDay = getElementValue("event-all-day", "checked");
  950.     gStartTimezone = (allDay ? floating(): calendarDefaultTimezone());
  951.     gEndTimezone = gStartTimezone;
  952.     gStartTime.timezone = gStartTimezone;
  953.     gEndTime.timezone = gEndTimezone;
  954.     updateAllDay();
  955. }
  956.  
  957. // this function sets the enabled/disabled
  958. // state of the following controls:
  959. // - 'event-starttime'
  960. // - 'event-endtime'
  961. // - 'timezone-starttime'
  962. // - 'timezone-endtime'
  963. // the state depends on whether or not the
  964. // event is configured as 'all-day' or not.
  965. function updateAllDay() {
  966.     if (gIgnoreUpdate) {
  967.         return;
  968.     }
  969.  
  970.     if (!isEvent(window.calendarItem)) {
  971.         return;
  972.     }
  973.  
  974.     var allDay = getElementValue("event-all-day", "checked");
  975.     setElementValue("event-starttime", allDay, "timepickerdisabled");
  976.     setElementValue("event-endtime", allDay, "timepickerdisabled");
  977.  
  978.     var tzStart = document.getElementById("timezone-starttime");
  979.     var tzEnd = document.getElementById("timezone-endtime");
  980.  
  981.     setShowTimeAs(allDay);
  982.  
  983.     gStartTime.isDate = allDay;
  984.     gEndTime.isDate = allDay;
  985.  
  986.     updateDateTime();
  987.     updateRepeatDetails();
  988.     updateAccept();
  989. }
  990.  
  991. function openNewEvent() {
  992.     var item = window.calendarItem;
  993.     var args = window.arguments[0];
  994.     args.onNewEvent(item.calendar);
  995. }
  996.  
  997. function openNewMessage() {
  998.     var msgComposeService = Components.classes["@mozilla.org/messengercompose;1"]
  999.                             .getService(Components.interfaces.nsIMsgComposeService);
  1000.     msgComposeService.OpenComposeWindow(null,
  1001.                                         null,
  1002.                                         Components.interfaces.nsIMsgCompType.New,
  1003.                                         Components.interfaces.nsIMsgCompFormat.Default,
  1004.                                         null,
  1005.                                         null);
  1006. }
  1007.  
  1008. function openNewCardDialog() {
  1009.     window.openDialog(
  1010.         "chrome://messenger/content/addressbook/abNewCardDialog.xul",
  1011.         "",
  1012.         "chrome,resizable=no,titlebar,modal");
  1013. }
  1014.  
  1015. // automatically select "show time as free" if this
  1016. // event is said to be all-day.
  1017. function setShowTimeAs(allDay) {
  1018.     gShowTimeAs = allDay ? "TRANSPARENT" : "OPAQUE";
  1019.     updateShowTimeAs();
  1020. }
  1021.  
  1022. function editAttendees() {
  1023.     var savedWindow = window;
  1024.     var calendar = document.getElementById("item-calendar")
  1025.                            .selectedItem.calendar;
  1026.  
  1027.     var callback = function(attendees, organizer, startTime, endTime) {
  1028.         savedWindow.attendees = attendees;
  1029.         if (organizer) {
  1030.             // In case we didn't have an organizer object before we
  1031.             // added attendees to our event we take the one created
  1032.             // by the 'invite attendee'-dialog.
  1033.             if (!savedWindow.organizer) {
  1034.                 savedWindow.organizer = organizer.clone();
  1035.             }
  1036.             // The other case is that we already had an organizer object
  1037.             // before we went throught the 'invite attendee'-dialog. In that
  1038.             // case make sure we don't carry over attributes that have been
  1039.             // set to their default values by the dialog but don't actually
  1040.             // exist in the original organizer object.
  1041.             if (!savedWindow.organizer.id) {
  1042.                 organizer.id = null;
  1043.             }
  1044.             if (!savedWindow.organizer.role) {
  1045.                 organizer.role = null;
  1046.             }
  1047.             if (!savedWindow.organizer.participationStatus) {
  1048.                 organizer.participationStatus = null;
  1049.             }
  1050.             if (!savedWindow.organizer.commonName) {
  1051.                 organizer.commonName = null;
  1052.             }
  1053.             savedWindow.organizer = organizer;
  1054.         }
  1055.         var duration = endTime.subtractDate(startTime);
  1056.         startTime = startTime.clone();
  1057.         endTime = endTime.clone();
  1058.         var kDefaultTimezone = calendarDefaultTimezone();
  1059.         gStartTimezone = startTime.timezone;
  1060.         gEndTimezone = endTime.timezone;
  1061.         gStartTime = startTime.getInTimezone(kDefaultTimezone);
  1062.         gEndTime = endTime.getInTimezone(kDefaultTimezone);
  1063.         gItemDuration = duration;
  1064.         updateAttendees();
  1065.         updateDateTime();
  1066.         if (isAllDay != gStartTime.isDate){
  1067.             setShowTimeAs(gStartTime.isDate)
  1068.         }
  1069.     };
  1070.  
  1071.     var startTime = gStartTime.getInTimezone(gStartTimezone);
  1072.     var endTime = gEndTime.getInTimezone(gEndTimezone);
  1073.  
  1074.     var isAllDay = getElementValue("event-all-day", "checked");
  1075.     if (isAllDay) {
  1076.         startTime.isDate = true;
  1077.         endTime.isDate = true;
  1078.         endTime.day += 1;
  1079.     } else {
  1080.         startTime.isDate = false;
  1081.         endTime.isDate = false;
  1082.     }
  1083.  
  1084.     var menuItem = document.getElementById('options-timezone-menuitem');
  1085.     var displayTimezone = menuItem.getAttribute('checked') == 'true';
  1086.  
  1087.     var args = new Object();
  1088.     args.startTime = startTime;
  1089.     args.endTime = endTime;
  1090.     args.displayTimezone = displayTimezone;
  1091.     args.attendees = window.attendees;
  1092.     args.organizer = window.organizer && window.organizer.clone();
  1093.     args.calendar = calendar;
  1094.     args.item = window.calendarItem;
  1095.     args.onOk = callback;
  1096.     args.fbWrapper = window.fbWrapper;
  1097.  
  1098.     // open the dialog modally
  1099.     openDialog(
  1100.         "chrome://calendar/content/sun-calendar-event-dialog-attendees.xul",
  1101.         "_blank",
  1102.         "chrome,titlebar,modal,resizable",
  1103.         args);
  1104. }
  1105.  
  1106. function editPrivacy(target) {
  1107.     gPrivacy = target.getAttribute("privacy");
  1108.  
  1109.     switch (gPrivacy) {
  1110.         case "PRIVATE":
  1111.             gShowTimeAs = "TRANSPARENT";
  1112.             break;
  1113.         case "CONFIDENTIAL":
  1114.         case "PUBLIC":
  1115.             gShowTimeAs = "OPAQUE";
  1116.             break;
  1117.     }
  1118.  
  1119.     updateShowTimeAs();
  1120.     updatePrivacy();
  1121. }
  1122.  
  1123. /**
  1124.  * This function updates the UI according to the global field 'gPrivacy' and the
  1125.  * selected calendar. If the selected calendar does not support privacy or only
  1126.  * certain values, these are removed from the UI. This function should be called
  1127.  * any time that gPrivacy is updated.
  1128.  */
  1129. function updatePrivacy() {
  1130.     var calendar = document.getElementById("item-calendar")
  1131.                            .selectedItem.calendar;
  1132.     var hasPrivacy = capSupported("privacy");
  1133.  
  1134.     if (hasPrivacy) {
  1135.         var numChilds;
  1136.         var privacyValues = capValues("privacy",
  1137.                                       ["PUBLIC", "CONFIDENTIAL", "PRIVATE"]);
  1138.  
  1139.         // Update privacy capabilities (toolbar)
  1140.         var menupopup = document.getElementById("event-privacy-menupopup");
  1141.         if (menupopup) {
  1142.             // Only update the toolbar if the button is actually there
  1143.             numChilds = menupopup.childNodes.length;
  1144.             for (var i = 0; i < numChilds; i++) {
  1145.                 var node = menupopup.childNodes[i];
  1146.                 if (node.hasAttribute("privacy")) {
  1147.                     var currentPrivacyValue = node.getAttribute("privacy");
  1148.                     // Collapsed state
  1149.  
  1150.                     // Hide the toolbar if the value is unsupported or is for a
  1151.                     // specific provider and doesn't belong to the current provider.
  1152.                     if (privacyValues.indexOf(currentPrivacyValue) < 0 ||
  1153.                         (currentProvider && currentProvider != calendar.type)) {
  1154.                         node.setAttribute("collapsed", "true");
  1155.                     } else {
  1156.                         node.removeAttribute("collapsed");
  1157.                     }
  1158.  
  1159.                     // Checked state
  1160.                     if (gPrivacy == currentPrivacyValue) {
  1161.                         node.setAttribute("checked", "true");
  1162.                     } else {
  1163.                         node.removeAttribute("checked");
  1164.                     }
  1165.                 }
  1166.             }
  1167.         }
  1168.  
  1169.         // Update privacy capabilities (menu)
  1170.         menupopup = document.getElementById("options-privacy-menupopup");
  1171.         numChilds = menupopup.childNodes.length;
  1172.         for (var i = 0; i < numChilds; i++) {
  1173.             var node = menupopup.childNodes[i];
  1174.             var currentProvider = node.getAttribute("provider");
  1175.             if (node.hasAttribute("privacy")) {
  1176.                 var currentPrivacyValue = node.getAttribute("privacy");
  1177.                 // Collapsed state
  1178.  
  1179.                 // Hide the menu if the value is unsupported or is for a
  1180.                 // specific provider and doesn't belong to the current provider.
  1181.                 if (privacyValues.indexOf(currentPrivacyValue) < 0 ||
  1182.                     (currentProvider && currentProvider != calendar.type)) {
  1183.                     node.setAttribute("collapsed", "true");
  1184.                 } else {
  1185.                     node.removeAttribute("collapsed");
  1186.                 }
  1187.  
  1188.                 // Checked state
  1189.                 if (gPrivacy == currentPrivacyValue) {
  1190.                     node.setAttribute("checked", "true");
  1191.                 } else {
  1192.                     node.removeAttribute("checked");
  1193.                 }
  1194.             }
  1195.         }
  1196.  
  1197.         // Update privacy capabilities (statusbar)
  1198.         var privacyPanel = document.getElementById("status-privacy");
  1199.         var hasAnyPrivacyValue = false;
  1200.         numChilds = privacyPanel.childNodes.length;
  1201.         for (var i = 0; i < numChilds; i++) {
  1202.             var node = privacyPanel.childNodes[i];
  1203.             var currentProvider = node.getAttribute("provider");
  1204.             if (node.hasAttribute("privacy")) {
  1205.                 var currentPrivacyValue = node.getAttribute("privacy");
  1206.  
  1207.                 // Hide the panel if the value is unsupported or is for a
  1208.                 // specific provider and doesn't belong to the current provider,
  1209.                 // or is not the items privacy value
  1210.                 if (privacyValues.indexOf(currentPrivacyValue) < 0 ||
  1211.                     (currentProvider && currentProvider != calendar.type) ||
  1212.                     gPrivacy != currentPrivacyValue) {
  1213.                     node.setAttribute("collapsed", "true");
  1214.                 } else {
  1215.                     node.removeAttribute("collapsed");
  1216.                     hasAnyPrivacyValue = true;
  1217.                 }
  1218.             }
  1219.         }
  1220.  
  1221.         // Don't show the status panel if no valid privacy value is selected
  1222.         if (!hasAnyPrivacyValue) {
  1223.             privacyPanel.setAttribute("collapsed", "true");
  1224.         } else {
  1225.             privacyPanel.removeAttribute("collapsed");
  1226.         }
  1227.  
  1228.     } else {
  1229.         setElementValue("button-privacy", !hasPrivacy && "true", "disabled");
  1230.         setElementValue("options-privacy-menu", !hasPrivacy && "true", "disabled");
  1231.         setElementValue("status-privacy", !hasPrivacy && "true", "collapsed");
  1232.     }
  1233. }
  1234.  
  1235. function editPriority(target) {
  1236.     gPriority = parseInt(target.getAttribute("value"));
  1237.     updatePriority();
  1238. }
  1239.  
  1240. function updatePriority() {
  1241.     // Set up capabilities
  1242.     var hasPriority = capSupported("priority");
  1243.     setElementValue("options-priority-menu", !hasPriority && "true", "disabled");
  1244.     setElementValue("status-priority", !hasPriority && "true", "collapsed");
  1245.  
  1246.     if (hasPriority) {
  1247.         var priorityLevel = "none";
  1248.         if (gPriority >= 1 && gPriority <= 4) {
  1249.             priorityLevel = "high";
  1250.         } else if (gPriority == 5) {
  1251.             priorityLevel = "normal";
  1252.         } else if (gPriority >= 6 && gPriority <= 9) {
  1253.             priorityLevel = "low";
  1254.         }
  1255.  
  1256.         var priorityNone = document.getElementById("cmd_priority_none");
  1257.         var priorityLow = document.getElementById("cmd_priority_low");
  1258.         var priorityNormal = document.getElementById("cmd_priority_normal");
  1259.         var priorityHigh = document.getElementById("cmd_priority_high");
  1260.  
  1261.         priorityNone.setAttribute("checked",
  1262.                                   priorityLevel == "none" ? "true" : "false");
  1263.         priorityLow.setAttribute("checked",
  1264.                                  priorityLevel == "low" ? "true" : "false");
  1265.         priorityNormal.setAttribute("checked",
  1266.                                     priorityLevel == "normal" ? "true" : "false");
  1267.         priorityHigh.setAttribute("checked",
  1268.                                   priorityLevel == "high" ? "true" : "false");
  1269.  
  1270.         // Status bar panel
  1271.         var priorityPanel = document.getElementById("status-priority");
  1272.         if (priorityLevel == "none") {
  1273.             // If the priority is none, don't show the status bar panel
  1274.             priorityPanel.setAttribute("collapsed", "true");
  1275.         } else {
  1276.             priorityPanel.removeAttribute("collapsed");
  1277.             var numChilds = priorityPanel.childNodes.length;
  1278.             var foundPriority = false;
  1279.             for (var i = 0; i < numChilds; i++) {
  1280.                 var node = priorityPanel.childNodes[i];
  1281.                 if (foundPriority) {
  1282.                     node.setAttribute("collapsed", "true");
  1283.                 } else {
  1284.                     node.removeAttribute("collapsed");
  1285.                 }
  1286.                 if (node.getAttribute("value") == priorityLevel) {
  1287.                     foundPriority = true;
  1288.                 }
  1289.             }
  1290.         }
  1291.     }
  1292. }
  1293.  
  1294. function editStatus(target) {
  1295.     gStatus = target.getAttribute("value");
  1296.     updateStatus();
  1297. }
  1298.  
  1299. function updateStatus() {
  1300.     [ "cmd_status_none",
  1301.       "cmd_status_tentative",
  1302.       "cmd_status_confirmed",
  1303.       "cmd_status_cancelled" ].forEach(
  1304.           function(element, index, array) {
  1305.               var node = document.getElementById(element);
  1306.               node.setAttribute("checked",
  1307.                   node.getAttribute("value") == gStatus ?
  1308.                       "true" : "false");
  1309.           }
  1310.       );
  1311. }
  1312.  
  1313. function editShowTimeAs(target) {
  1314.     gShowTimeAs = target.getAttribute("value");
  1315.     updateShowTimeAs();
  1316. }
  1317.  
  1318. function updateShowTimeAs() {
  1319.     var showAsBusy = document.getElementById("cmd_showtimeas_busy");
  1320.     var showAsFree = document.getElementById("cmd_showtimeas_free");
  1321.  
  1322.     showAsBusy.setAttribute("checked",
  1323.                             gShowTimeAs == "OPAQUE" ? "true" : "false");
  1324.     showAsFree.setAttribute("checked",
  1325.                             gShowTimeAs == "TRANSPARENT" ? "true" : "false");
  1326. }
  1327.  
  1328. function editURL() {
  1329.     var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  1330.                        .getService(Components.interfaces.nsIPromptService);
  1331.     if (promptService) {
  1332.         // ghost in an example...
  1333.         if (!gURL) {
  1334.             gURL = "http://www.example.com";
  1335.         }
  1336.         var result = { value: gURL };
  1337.         if (promptService.prompt(
  1338.             window,
  1339.             calGetString("sun-calendar-event-dialog", "specifyLinkLocation"),
  1340.             calGetString("sun-calendar-event-dialog", "enterLinkLocation"),
  1341.             result,
  1342.             null,
  1343.             { value: 0 })) {
  1344.             var url = result.value;
  1345.             // The user might have just put in 'www.foo.com', correct that here
  1346.             if (url != "" && url.indexOf( ":" ) == -1) {
  1347.                 url = "http://" + url;
  1348.             }
  1349.             gURL = url;
  1350.             updateDocument();
  1351.         }
  1352.     }
  1353. }
  1354.  
  1355. function updateCalendar() {
  1356.     var item = window.calendarItem;
  1357.     var calendar = document.getElementById("item-calendar")
  1358.                            .selectedItem.calendar;
  1359.  
  1360.     gIsReadOnly = true;
  1361.     if (calendar) {
  1362.         gIsReadOnly = calendar.readOnly;
  1363.     }
  1364.  
  1365.     if (calendar.sendItipInvitations) {
  1366.         enableElement("send-invitations-checkbox");
  1367.     } else {
  1368.         disableElement("send-invitations-checkbox");
  1369.     }
  1370.  
  1371.     // update the accept button
  1372.     updateAccept();
  1373.  
  1374.     // TODO: the code above decided about whether or not the item is readonly.
  1375.     // below we enable/disable all controls based on this decision.
  1376.     // unfortunately some controls need to be disabled based on some other
  1377.     // criteria. this is why we enable all controls in case the item is *not*
  1378.     // readonly and run through all those updateXXX() functions to disable
  1379.     // them again based on the specific logic build into those function. is this
  1380.     // really a good idea?
  1381.     if (gIsReadOnly) {
  1382.         var disableElements = document.getElementsByAttribute("disable-on-readonly", "true");
  1383.         for (var i = 0; i < disableElements.length; i++) {
  1384.             disableElements[i].setAttribute('disabled', 'true');
  1385.  
  1386.             // we mark link-labels with the hyperlink attribute, since we need
  1387.             // to remove their class in case they get disabled. TODO: it would
  1388.             // be better to create a small binding for those link-labels
  1389.             // instead of adding those special stuff.
  1390.             if (disableElements[i].hasAttribute('hyperlink')) {
  1391.                 disableElements[i].removeAttribute('class');
  1392.                 disableElements[i].removeAttribute('onclick');
  1393.             }
  1394.         }
  1395.  
  1396.         var collapseElements = document.getElementsByAttribute("collapse-on-readonly", "true");
  1397.         for (var i = 0; i < collapseElements.length; i++) {
  1398.             collapseElements[i].setAttribute('collapsed', 'true');
  1399.         }
  1400.     } else {
  1401.         var enableElements = document.getElementsByAttribute("disable-on-readonly", "true");
  1402.         for (var i = 0; i < enableElements.length; i++) {
  1403.             enableElements[i].removeAttribute('disabled');
  1404.             if (enableElements[i].hasAttribute('hyperlink')) {
  1405.                 enableElements[i].setAttribute('class', 'text-link');
  1406.             }
  1407.         }
  1408.  
  1409.         var collapseElements = document.getElementsByAttribute("collapse-on-readonly", "true");
  1410.         for (var i = 0; i < collapseElements.length; i++) {
  1411.             collapseElements[i].removeAttribute('collapsed');
  1412.         }
  1413.  
  1414.         // Task completed date
  1415.         if (item.completedDate) {
  1416.             updateToDoStatus(item.status, item.completedDate.jsDate);
  1417.         } else {
  1418.             updateToDoStatus(item.status);
  1419.         }
  1420.  
  1421.         // disable repeat menupopup if this is an occurrence
  1422.         var item = window.calendarItem;
  1423.         if (item.parentItem != item) {
  1424.             disableElement("item-repeat");
  1425.             var repeatDetails = document.getElementById("repeat-details");
  1426.             var numChilds = repeatDetails.childNodes.length;
  1427.             for (var i = 0; i < numChilds; i++) {
  1428.                 var node = repeatDetails.childNodes[i];
  1429.                 node.setAttribute('disabled', 'true');
  1430.                 node.removeAttribute('class');
  1431.                 node.removeAttribute('onclick');
  1432.             }
  1433.         }
  1434.  
  1435.         // If the item is a proxy occurrence/instance, a few things aren't
  1436.         // valid.
  1437.         if (item.parentItem != item) {
  1438.             setElementValue("item-calendar", "true", "disabled");
  1439.  
  1440.             // don't allow to revoke the entrydate of recurring todo's.
  1441.             disableElementWithLock("todo-has-entrydate", "permanent-lock");
  1442.         }
  1443.  
  1444.         // update datetime pickers
  1445.         updateDueDate();
  1446.         updateEntryDate();
  1447.  
  1448.         // update datetime pickers
  1449.         updateAllDay();
  1450.     }
  1451.  
  1452.     // Make sure capabilties are reflected correctly
  1453.     updateCapabilities();
  1454.  
  1455. }
  1456.  
  1457. function editRepeat() {
  1458.     var args = new Object();
  1459.     args.calendarEvent = window.calendarItem;
  1460.     args.recurrenceInfo = window.recurrenceInfo;
  1461.     args.startTime = gStartTime;
  1462.     args.endTime = gEndTime;
  1463.  
  1464.     var savedWindow = window;
  1465.     args.onOk = function(recurrenceInfo) {
  1466.         savedWindow.recurrenceInfo = recurrenceInfo;
  1467.     };
  1468.  
  1469.     window.setCursor("wait");
  1470.  
  1471.     // open the dialog modally
  1472.     openDialog(
  1473.         "chrome://calendar/content/sun-calendar-event-dialog-recurrence.xul",
  1474.         "_blank",
  1475.         "chrome,titlebar,modal,resizable",
  1476.         args);
  1477. }
  1478.  
  1479. /**
  1480.  * This function is responsilble for propagating UI state to controls
  1481.  * depending on the repeat setting of an item. This functionality is used
  1482.  * after the dialog has been loaded as well as if the repeat pattern has
  1483.  * been changed.
  1484.  */
  1485. function updateRepeat() {
  1486.     var repeatMenu = document.getElementById("item-repeat");
  1487.     var repeatItem = repeatMenu.selectedItem;
  1488.     var repeatValue = repeatItem.getAttribute("value");
  1489.  
  1490.     if (repeatValue == 'none') {
  1491.         window.recurrenceInfo = null;
  1492.         var item = window.calendarItem;
  1493.         if (isToDo(item)) {
  1494.             enableElementWithLock("todo-has-entrydate", "repeat-lock");
  1495.         }
  1496.     } else if (repeatValue == 'custom') {
  1497.         // the user selected custom repeat pattern. we now need to bring
  1498.         // up the appropriate dialog in order to let the user specify the
  1499.         // new rule. first of all, retrieve the item we want to specify
  1500.         // the custom repeat pattern for.
  1501.         var item = window.calendarItem;
  1502.  
  1503.         // if this item is a task, we need to make sure that it has
  1504.         // an entry-date, otherwise we can't create a recurrence.
  1505.         if (isToDo(item)) {
  1506.             // automatically check 'has entrydate' if needed.
  1507.             if (!getElementValue("todo-has-entrydate", "checked")) {
  1508.                 setElementValue("todo-has-entrydate", "true", "checked");
  1509.  
  1510.                 // make sure gStartTime is properly initialized
  1511.                 updateEntryDate();
  1512.             }
  1513.  
  1514.             // disable the checkbox to indicate that we need
  1515.             // the entry-date. the 'disabled' state will be
  1516.             // revoked if the user turns off the repeat pattern.
  1517.             disableElementWithLock("todo-has-entrydate", "repeat-lock");
  1518.         }
  1519.  
  1520.         // retrieve the current recurrence info, we need this
  1521.         // to find out whether or not the user really created
  1522.         // a new repeat pattern.
  1523.         var recurrenceInfo = window.recurrenceInfo;
  1524.  
  1525.         // now bring up the recurrence dialog.
  1526.         // don't pop up the dialog if this happens during
  1527.         // initialization of the dialog.
  1528.         if (repeatMenu.hasAttribute("last-value")) {
  1529.             editRepeat();
  1530.         }
  1531.  
  1532.         // we need to address two separate cases here.
  1533.         // 1) we need to revoke the selection of the repeat
  1534.         //    drop down list in case the user didn't specify
  1535.         //    a new repeat pattern (i.e. canceled the dialog)
  1536.         // 2) re-enable the 'has entrydate' option in case
  1537.         //    we didn't end up with a recurrence rule.
  1538.         if (recurrenceInfo == window.recurrenceInfo) {
  1539.             repeatMenu.selectedIndex = gLastRepeatSelection;
  1540.             if (isToDo(item)) {
  1541.                 if (!window.recurrenceInfo) {
  1542.                     enableElementWithLock("todo-has-entrydate", "repeat-lock");
  1543.                 }
  1544.             }
  1545.         }
  1546.     } else {
  1547.         var item = window.calendarItem;
  1548.         var recurrenceInfo = window.recurrenceInfo || item.recurrenceInfo;
  1549.         if (recurrenceInfo) {
  1550.             recurrenceInfo = recurrenceInfo.clone();
  1551.             var rrules = splitRecurrenceRules(recurrenceInfo);
  1552.             if (rrules[0].length > 0) {
  1553.                 recurrenceInfo.deleteRecurrenceItem(rrules[0][0]);
  1554.             }
  1555.         } else {
  1556.             recurrenceInfo = createRecurrenceInfo(item);
  1557.         }
  1558.  
  1559.         switch (repeatValue) {
  1560.             case 'daily':
  1561.               var recRule = createRecurrenceRule();
  1562.               recRule.type = 'DAILY';
  1563.               recRule.interval = 1;
  1564.               recRule.count = -1;
  1565.               break;
  1566.             case 'weekly':
  1567.               var recRule = createRecurrenceRule();
  1568.               recRule.type = 'WEEKLY';
  1569.               recRule.interval = 1;
  1570.               recRule.count = -1;
  1571.               break;
  1572.             case 'every.weekday':
  1573.               var recRule = createRecurrenceRule();
  1574.               recRule.type = 'DAILY';
  1575.               recRule.interval = 1;
  1576.               recRule.count = -1;
  1577.               var onDays = [2, 3, 4, 5, 6];
  1578.               recRule.setComponent("BYDAY", onDays.length, onDays);
  1579.               break;
  1580.             case 'bi.weekly':
  1581.               var recRule = createRecurrenceRule();
  1582.               recRule.type = 'WEEKLY';
  1583.               recRule.interval = 2;
  1584.               recRule.count = -1;
  1585.               break;
  1586.             case 'monthly':
  1587.               var recRule = createRecurrenceRule();
  1588.               recRule.type = 'MONTHLY';
  1589.               recRule.interval = 1;
  1590.               recRule.count = -1;
  1591.               break;
  1592.             case 'yearly':
  1593.               var recRule = createRecurrenceRule();
  1594.               recRule.type = 'YEARLY';
  1595.               recRule.interval = 1;
  1596.               recRule.count = -1;
  1597.               break;
  1598.         }
  1599.  
  1600.         recurrenceInfo.insertRecurrenceItemAt(recRule, 0);
  1601.         window.recurrenceInfo = recurrenceInfo;
  1602.  
  1603.         if (isToDo(item)) {
  1604.             if (!getElementValue("todo-has-entrydate", "checked")) {
  1605.                 setElementValue("todo-has-entrydate", "true", "checked");
  1606.             }
  1607.             disableElementWithLock("todo-has-entrydate", "repeat-lock");
  1608.         }
  1609.     }
  1610.  
  1611.     gLastRepeatSelection = repeatMenu.selectedIndex;
  1612.     repeatMenu.setAttribute("last-value", repeatValue);
  1613.  
  1614.     updateRepeatDetails();
  1615.     updateEntryDate();
  1616.     updateDueDate();
  1617.     updateAccept();
  1618. }
  1619.  
  1620. function updateToDoStatus(status, passedInCompletedDate) {
  1621.   // RFC2445 doesn't support completedDates without the todo's status
  1622.   // being "COMPLETED", however twiddling the status menulist shouldn't
  1623.   // destroy that information at this point (in case you change status
  1624.   // back to COMPLETED). When we go to store this VTODO as .ics the
  1625.   // date will get lost.
  1626.  
  1627.   var completedDate;
  1628.   if (passedInCompletedDate) {
  1629.       completedDate = passedInCompletedDate;
  1630.   } else {
  1631.       completedDate = null;
  1632.   }
  1633.  
  1634.   // remember the original values
  1635.   var oldPercentComplete = getElementValue("percent-complete-textbox");
  1636.   var oldCompletedDate   = getElementValue("completed-date-picker");
  1637.  
  1638.   switch (status) {
  1639.       case null:
  1640.       case "":
  1641.       case "NONE":
  1642.           document.getElementById("todo-status").selectedIndex = 0;
  1643.           disableElement("percent-complete-textbox");
  1644.           disableElement("percent-complete-label");
  1645.           break;
  1646.       case "CANCELLED":
  1647.           document.getElementById("todo-status").selectedIndex = 4;
  1648.           disableElement("percent-complete-textbox");
  1649.           disableElement("percent-complete-label");
  1650.           break;
  1651.       case "COMPLETED":
  1652.           document.getElementById("todo-status").selectedIndex = 3;
  1653.           enableElement("percent-complete-textbox");
  1654.           enableElement("percent-complete-label");
  1655.           // if there isn't a completedDate, set it to now
  1656.           if (!completedDate)
  1657.               completedDate = new Date();
  1658.           break;
  1659.       case "IN-PROCESS":
  1660.           document.getElementById("todo-status").selectedIndex = 2;
  1661.           disableElement("completed-date-picker");
  1662.           enableElement("percent-complete-textbox");
  1663.           enableElement("percent-complete-label");
  1664.           break;
  1665.       case "NEEDS-ACTION":
  1666.           document.getElementById("todo-status").selectedIndex = 1;
  1667.           enableElement("percent-complete-textbox");
  1668.           enableElement("percent-complete-label");
  1669.           break;
  1670.   }
  1671.  
  1672.   if (status == "COMPLETED") {
  1673.       setElementValue("percent-complete-textbox", "100");
  1674.       setElementValue("completed-date-picker", completedDate);
  1675.       enableElement("completed-date-picker");
  1676.   } else {
  1677.       if (oldPercentComplete != 100) {
  1678.           setElementValue("percent-complete-textbox", oldPercentComplete);
  1679.       } else {
  1680.           setElementValue("percent-complete-textbox", "");
  1681.       }
  1682.       setElementValue("completed-date-picker", oldCompletedDate);
  1683.       disableElement("completed-date-picker");
  1684.   }
  1685. }
  1686.  
  1687. function saveItem() {
  1688.     // we need to clone the item in order to apply the changes.
  1689.     // it is important to not apply the changes to the original item
  1690.     // (even if it happens to be mutable) in order to guarantee
  1691.     // that providers see a proper oldItem/newItem pair in case
  1692.     // they rely on this fact (e.g. WCAP does).
  1693.     var originalItem = window.calendarItem;
  1694.     var item = originalItem.clone();
  1695.  
  1696.     // override item's recurrenceInfo *before* serializing date/time-objects.
  1697.     if (!window.isOccurrence) {
  1698.         item.recurrenceInfo = window.recurrenceInfo;
  1699.     }
  1700.  
  1701.     // serialize the item
  1702.     saveDialog(item);
  1703.  
  1704.     // we set the organizer of this item only if
  1705.     // it is a stand-alone instance [not an occurrence].
  1706.     if (!window.isOccurrence) {
  1707.         item.organizer = window.organizer;
  1708.     }
  1709.  
  1710.     if (window.attendees) {
  1711.         item.removeAllAttendees();
  1712.         for each (var attendee in window.attendees) {
  1713.            item.addAttendee(attendee);
  1714.         }
  1715.  
  1716.         var sendInvitesCheckbox = document.getElementById("send-invitations-checkbox");
  1717.         if (sendInvitesCheckbox.checked) {
  1718.             setItemProperty(item, "X-MOZ-SEND-INVITATIONS", "TRUE");
  1719.         } else {
  1720.             item.deleteProperty("X-MOZ-SEND-INVITATIONS");
  1721.         }
  1722.     }
  1723.  
  1724.     return item;
  1725. }
  1726.  
  1727. function onCommandSave(aIsClosing) {
  1728.     var originalItem = window.calendarItem;
  1729.     var item = saveItem();
  1730.     var calendar = document.getElementById("item-calendar")
  1731.                            .selectedItem.calendar;
  1732.  
  1733.     item.makeImmutable();
  1734.     // Set the item for now, the callback below will set the full item when the
  1735.     // call succeeded
  1736.     window.calendarItem = item;
  1737.  
  1738.     // When the call is complete, we need to set the new item, so that the
  1739.     // dialog is up to date.
  1740.  
  1741.     // XXX Do we want to disable the dialog or at least the save button until
  1742.     // the call is complete? This might help when the user tries to save twice
  1743.     // before the call is complete. In that case, we do need a progress bar and
  1744.     // the ability to cancel the operation though.
  1745.     var listener = {
  1746.         onOperationComplete: function(aCalendar, aStatus, aOpType, aId, aItem) {
  1747.             if (Components.isSuccessCode(aStatus)) {
  1748.                 window.calendarItem = aItem;
  1749.             }
  1750.         }
  1751.     };
  1752.  
  1753.     // Let the caller decide how to handle the modified/added item. Only pass
  1754.     // the above item if we are not closing, otherwise the listener will be
  1755.     // missing its window afterwards.
  1756.     window.onAcceptCallback(item, calendar, originalItem, !aIsClosing && listener);
  1757.  
  1758. }
  1759.  
  1760. function onCommandExit() {
  1761.     // the correct way would be to hook 'onCancel' to the
  1762.     // 'tryToClose' attribute, but if the user wants to save
  1763.     // the changes we're running into trouble since the calendar
  1764.     // engine won't exit any longer, which results in dataloss.
  1765.     // window.tryToClose = onCancel;
  1766.     if (onCancel()) {
  1767.         goQuitApplication()
  1768.     }
  1769. }
  1770.  
  1771. function onCommandViewToolbar(aToolbarId, aMenuItemId) {
  1772.     var toolbar = document.getElementById(aToolbarId);
  1773.     var menuItem = document.getElementById(aMenuItemId);
  1774.  
  1775.     if (!toolbar || !menuItem) {
  1776.         return;
  1777.     }
  1778.  
  1779.     var toolbarCollapsed = toolbar.collapsed;
  1780.  
  1781.     // toggle the checkbox
  1782.     menuItem.setAttribute('checked', toolbarCollapsed);
  1783.  
  1784.     // toggle visibility of the toolbar
  1785.     toolbar.collapsed = !toolbarCollapsed;
  1786.  
  1787.     document.persist(aToolbarId, 'collapsed');
  1788.     document.persist(aMenuItemId, 'checked');
  1789. }
  1790.  
  1791. /**
  1792.  * DialogToolboxCustomizeDone() is called after the customize toolbar dialog
  1793.  * has been closed by the user. We need to restore the state of all buttons
  1794.  * and commands of all customizable toolbars.
  1795.  */
  1796.  
  1797. function DialogToolboxCustomizeDone(aToolboxChanged) {
  1798.  
  1799.     var menubar = document.getElementById("event-menubar");
  1800.     for (var i = 0; i < menubar.childNodes.length; ++i) {
  1801.         menubar.childNodes[i].removeAttribute("disabled");
  1802.     }
  1803.   
  1804.     // make sure our toolbar buttons have the correct enabled state restored to them...
  1805.     document.commandDispatcher.updateCommands('itemCommands');
  1806.  
  1807.     // Enable the toolbar context menu items
  1808.     document.getElementById("cmd_customize").removeAttribute("disabled");
  1809.  
  1810.     // Update privacy items to make sure the toolbarbutton's menupopup is set
  1811.     // correctly
  1812.     updatePrivacy();
  1813. }
  1814.  
  1815. function onCommandCustomize() {
  1816.     // install the callback that handles what needs to be
  1817.     // done after a toolbar has been customized.
  1818.     var toolbox = document.getElementById("event-toolbox");
  1819.     toolbox.customizeDone = DialogToolboxCustomizeDone;
  1820.  
  1821.     var menubar = document.getElementById("event-menubar");
  1822.     for (var i = 0; i < menubar.childNodes.length; ++i) {
  1823.         menubar.childNodes[i].setAttribute("disabled", true);
  1824.     }
  1825.       
  1826.     // Disable the toolbar context menu items
  1827.     document.getElementById("cmd_customize").setAttribute("disabled", "true");
  1828.  
  1829.     var id = "event-toolbox";
  1830.     if (gIsSunbird) {
  1831. //@line 1832 "/cygdrive/c/builds/tinderbox/Lt-Mozilla1.8/WINNT_5.2_Depend/mozilla/calendar/prototypes/wcap/sun-calendar-event-dialog.js"
  1832.         var newwindow = window.openDialog("chrome://calendar/content/customizeToolbar.xul",
  1833.                                           "CustomizeToolbar",
  1834.                                           "chrome,all,dependent",
  1835.                                           document.getElementById(id));
  1836. //@line 1842 "/cygdrive/c/builds/tinderbox/Lt-Mozilla1.8/WINNT_5.2_Depend/mozilla/calendar/prototypes/wcap/sun-calendar-event-dialog.js"
  1837.     } else {
  1838.         var wintype = document.documentElement.getAttribute("windowtype");
  1839.         wintype = wintype.replace(/:/g, "");
  1840.  
  1841.         window.openDialog("chrome://global/content/customizeToolbar.xul",
  1842.                           "CustomizeToolbar" + wintype,
  1843.                           "chrome,all,dependent",
  1844.                           document.getElementById(id), // toolbar dom node
  1845.                           false,                       // is mode toolbar yes/no?
  1846.                           null,                        // callback function
  1847.                           "dialog");                   // name of this mode
  1848.     }
  1849. }
  1850.  
  1851. function editStartTimezone() {
  1852.     editTimezone(
  1853.         "timezone-starttime",
  1854.         gStartTime.getInTimezone(gStartTimezone),
  1855.         function(datetime) {
  1856.             var equalTimezones = false;
  1857.             if (gStartTimezone && gEndTimezone) {
  1858.                 if (gStartTimezone == gEndTimezone) {
  1859.                     equalTimezones = true;
  1860.                 }
  1861.             }
  1862.             gStartTimezone = datetime.timezone;
  1863.             if (equalTimezones) {
  1864.               gEndTimezone = datetime.timezone;
  1865.             }
  1866.             updateDateTime();
  1867.         });
  1868. }
  1869.  
  1870. function editEndTimezone() {
  1871.     editTimezone(
  1872.         "timezone-endtime",
  1873.         gEndTime.getInTimezone(gEndTimezone),
  1874.         function(datetime) {
  1875.             var equalTimezones = false;
  1876.             if (gStartTimezone && gEndTimezone) {
  1877.                 if (compareObjects(gStartTimezone, gEndTimezone)) {
  1878.                     equalTimezones = true;
  1879.                 }
  1880.             }
  1881.             if (equalTimezones) {
  1882.                 gStartTimezone = datetime.timezone;
  1883.             }
  1884.             gEndTimezone = datetime.timezone;
  1885.             updateDateTime();
  1886.         });
  1887. }
  1888.  
  1889. function editTimezone(aElementId,aDateTime,aCallback) {
  1890.     if (document.getElementById(aElementId)
  1891.         .hasAttribute("disabled")) {
  1892.         return;
  1893.     }
  1894.  
  1895.     // prepare the arguments that will be passed to the dialog
  1896.     var args = new Object();
  1897.     args.time = aDateTime;
  1898.     args.onOk = aCallback;
  1899.  
  1900.     // open the dialog modally
  1901.     openDialog(
  1902.         "chrome://calendar/content/sun-calendar-event-dialog-timezone.xul",
  1903.         "_blank",
  1904.         "chrome,titlebar,modal,resizable",
  1905.         args);
  1906. }
  1907.  
  1908. // this function initializes the following controls:
  1909. // - 'event-starttime'
  1910. // - 'event-endtime'
  1911. // - 'event-all-day'
  1912. // - 'todo-has-entrydate'
  1913. // - 'todo-entrydate'
  1914. // - 'todo-has-duedate'
  1915. // - 'todo-duedate'
  1916. // the date/time-objects are either displayed in their repective
  1917. // timezone or in the default timezone. this decision is based
  1918. // on whether or not 'options-timezone-menuitem' is checked.
  1919. // the necessary information is taken from the following variables:
  1920. // - 'gStartTime'
  1921. // - 'gEndTime'
  1922. // - 'window.calendarItem' (used to decide about event/task)
  1923. function updateDateTime() {
  1924.     gIgnoreUpdate = true;
  1925.  
  1926.     var item = window.calendarItem;
  1927.     var menuItem = document.getElementById('options-timezone-menuitem');
  1928.  
  1929.     // convert to default timezone if the timezone option
  1930.     // is *not* checked, otherwise keep the specific timezone
  1931.     // and display the labels in order to modify the timezone.
  1932.     if (menuItem.getAttribute('checked') == 'true') {
  1933.         if (isEvent(item)) {
  1934.           var startTime = gStartTime.getInTimezone(gStartTimezone);
  1935.           var endTime = gEndTime.getInTimezone(gEndTimezone);
  1936.  
  1937.           setElementValue("event-all-day", startTime.isDate, "checked");
  1938.  
  1939.           // in the case where the timezones are different but
  1940.           // the timezone of the endtime is "UTC", we convert
  1941.           // the endtime into the timezone of the starttime.
  1942.           if (startTime && endTime) {
  1943.             if (!compareObjects(startTime.timezone, endTime.timezone)) {
  1944.               if (endTime.timezone.isUTC) {
  1945.                 endTime = endTime.getInTimezone(startTime.timezone);
  1946.               }
  1947.             }
  1948.           }
  1949.  
  1950.           // before feeding the date/time value into the control we need
  1951.           // to set the timezone to 'floating' in order to avoid the
  1952.           // automatic conversion back into the OS timezone.
  1953.           startTime.timezone = floating();
  1954.           endTime.timezone = floating();
  1955.  
  1956.           setElementValue("event-starttime", startTime.jsDate);
  1957.           setElementValue("event-endtime", endTime.jsDate);
  1958.         }
  1959.  
  1960.         if (isToDo(item)) {
  1961.           var startTime = gStartTime && gStartTime.getInTimezone(gStartTimezone);
  1962.           var endTime = gEndTime && gEndTime.getInTimezone(gEndTimezone);
  1963.           var hasEntryDate = (startTime != null);
  1964.           var hasDueDate = (endTime != null);
  1965.  
  1966.           if (hasEntryDate && hasDueDate) {
  1967.               setElementValue("todo-has-entrydate", hasEntryDate, "checked");
  1968.               startTime.timezone = floating();
  1969.               setElementValue("todo-entrydate", startTime.jsDate);
  1970.  
  1971.               setElementValue("todo-has-duedate", hasDueDate, "checked");
  1972.               endTime.timezone = floating();
  1973.               setElementValue("todo-duedate", endTime.jsDate);
  1974.           } else if (hasEntryDate) {
  1975.               setElementValue("todo-has-entrydate", hasEntryDate, "checked");
  1976.               startTime.timezone = floating();
  1977.               setElementValue("todo-entrydate", startTime.jsDate);
  1978.  
  1979.               startTime.timezone = floating();
  1980.               setElementValue("todo-duedate", startTime.jsDate);
  1981.           } else if (hasDueDate) {
  1982.               endTime.timezone = floating();
  1983.               setElementValue("todo-entrydate", endTime.jsDate);
  1984.  
  1985.               setElementValue("todo-has-duedate", hasDueDate, "checked");
  1986.               endTime.timezone = floating();
  1987.               setElementValue("todo-duedate", endTime.jsDate);
  1988.           } else {
  1989.               // The time for the todo should default to the next full hour
  1990.               startTime = now();
  1991.               startTime.timezone = floating();
  1992.               startTime.minute = 0;
  1993.               startTime.second = 0;
  1994.               startTime.hour++;
  1995.               endTime = startTime.clone();
  1996.  
  1997.               setElementValue("todo-entrydate", startTime.jsDate);
  1998.               setElementValue("todo-duedate", endTime.jsDate);
  1999.           }
  2000.         }
  2001.     } else {
  2002.         var kDefaultTimezone = calendarDefaultTimezone();
  2003.  
  2004.         if (isEvent(item)) {
  2005.             var startTime = gStartTime.getInTimezone(kDefaultTimezone);
  2006.             var endTime = gEndTime.getInTimezone(kDefaultTimezone);
  2007.             setElementValue("event-all-day", startTime.isDate, "checked");
  2008.  
  2009.             // before feeding the date/time value into the control we need
  2010.             // to set the timezone to 'floating' in order to avoid the
  2011.             // automatic conversion back into the OS timezone.
  2012.             startTime.timezone = floating();
  2013.             endTime.timezone = floating();
  2014.             setElementValue("event-starttime", startTime.jsDate);
  2015.             setElementValue("event-endtime", endTime.jsDate);
  2016.         }
  2017.  
  2018.         if (isToDo(item)) {
  2019.             var startTime = gStartTime &&
  2020.                             gStartTime.getInTimezone(kDefaultTimezone);
  2021.             var endTime = gEndTime && gEndTime.getInTimezone(kDefaultTimezone);
  2022.             var hasEntryDate = (startTime != null);
  2023.             var hasDueDate = (endTime != null);
  2024.  
  2025.             if (hasEntryDate && hasDueDate) {
  2026.                 setElementValue("todo-has-entrydate", hasEntryDate, "checked");
  2027.                 startTime.timezone = floating();
  2028.                 setElementValue("todo-entrydate", startTime.jsDate);
  2029.  
  2030.                 setElementValue("todo-has-duedate", hasDueDate, "checked");
  2031.                 endTime.timezone = floating();
  2032.                 setElementValue("todo-duedate", endTime.jsDate);
  2033.             } else if (hasEntryDate) {
  2034.                 setElementValue("todo-has-entrydate", hasEntryDate, "checked");
  2035.                 startTime.timezone = floating();
  2036.                 setElementValue("todo-entrydate", startTime.jsDate);
  2037.  
  2038.                 startTime.timezone = floating();
  2039.                 setElementValue("todo-duedate", startTime.jsDate);
  2040.             } else if (hasDueDate) {
  2041.                 endTime.timezone = floating();
  2042.                 setElementValue("todo-entrydate", endTime.jsDate);
  2043.  
  2044.                 setElementValue("todo-has-duedate", hasDueDate, "checked");
  2045.                 endTime.timezone = floating();
  2046.                 setElementValue("todo-duedate", endTime.jsDate);
  2047.             } else {
  2048.                 // The time for the todo should default to the next full hour
  2049.                 startTime = now();
  2050.                 startTime.timezone = floating();
  2051.                 startTime.minute = 0;
  2052.                 startTime.second = 0;
  2053.                 startTime.hour++;
  2054.                 endTime = startTime.clone();
  2055.  
  2056.                 setElementValue("todo-entrydate", startTime.jsDate);
  2057.                 setElementValue("todo-duedate", endTime.jsDate);
  2058.             }
  2059.         }
  2060.     }
  2061.  
  2062.     updateTimezone();
  2063.     updateAllDay();
  2064.  
  2065.     gIgnoreUpdate = false;
  2066. }
  2067.  
  2068. // this function initializes the following controls:
  2069. // - 'timezone-starttime'
  2070. // - 'timezone-endtime'
  2071. // the timezone-links show the corrosponding names of the
  2072. // start/end times. if 'options-timezone-menuitem' is not checked
  2073. // the links will be collapsed.
  2074. function updateTimezone() {
  2075.     var menuItem = document.getElementById('options-timezone-menuitem');
  2076.  
  2077.     // convert to default timezone if the timezone option
  2078.     // is *not* checked, otherwise keep the specific timezone
  2079.     // and display the labels in order to modify the timezone.
  2080.     if (menuItem.getAttribute('checked') == 'true') {
  2081.         var startTimezone = gStartTimezone;
  2082.         var endTimezone = gEndTimezone;
  2083.  
  2084.         var equalTimezones = false;
  2085.         if (startTimezone && endTimezone) {
  2086.             if (compareObjects(startTimezone, endTimezone) || endTimezone.isUTC) {
  2087.                 equalTimezones = true;
  2088.             }
  2089.         }
  2090.  
  2091.         function updateTimezoneElement(aTimezone,aId,aDateTime,aCollapse) {
  2092.             var element = document.getElementById(aId);
  2093.             if (element) {
  2094.                 if (aTimezone != null && !aCollapse) {
  2095.                     element.removeAttribute('collapsed');
  2096.                     element.value = timezoneString(aTimezone);
  2097.                     if (!aDateTime || !aDateTime.isValid || gIsReadOnly || aDateTime.isDate) {
  2098.                         if (element.hasAttribute('class')) {
  2099.                             element.setAttribute('class-on-enabled',
  2100.                                 element.getAttribute('class'));
  2101.                             element.removeAttribute('class');
  2102.                         }
  2103.                         if (element.hasAttribute('onclick')) {
  2104.                             element.setAttribute('onclick-on-enabled',
  2105.                                 element.getAttribute('onclick'));
  2106.                             element.removeAttribute('onclick');
  2107.                         }
  2108.                         element.setAttribute('disabled', 'true');
  2109.                     } else {
  2110.                         if (element.hasAttribute('class-on-enabled')) {
  2111.                             element.setAttribute('class',
  2112.                                 element.getAttribute('class-on-enabled'));
  2113.                             element.removeAttribute('class-on-enabled');
  2114.                         }
  2115.                         if (element.hasAttribute('onclick-on-enabled')) {
  2116.                             element.setAttribute('onclick',
  2117.                                 element.getAttribute('onclick-on-enabled'));
  2118.                             element.removeAttribute('onclick-on-enabled');
  2119.                         }
  2120.                         element.removeAttribute('disabled');
  2121.                     }
  2122.                 } else {
  2123.                     element.setAttribute('collapsed', 'true');
  2124.                 }
  2125.             }
  2126.         }
  2127.         
  2128.         updateTimezoneElement(startTimezone,
  2129.                               'timezone-starttime',
  2130.                               gStartTime,
  2131.                               false);
  2132.         updateTimezoneElement(endTimezone,
  2133.                               'timezone-endtime',
  2134.                               gEndTime,
  2135.                               equalTimezones);
  2136.     } else {
  2137.         document.getElementById('timezone-starttime')
  2138.                 .setAttribute('collapsed', 'true');
  2139.         document.getElementById('timezone-endtime')
  2140.                 .setAttribute('collapsed', 'true');
  2141.     }
  2142. }
  2143.  
  2144. function updateDocument() {
  2145.     var hasAttachments = capSupported("attachments");
  2146.     setElementValue("cmd_url", !hasAttachments && "true", "disabled");
  2147.  
  2148.     var documentRow = document.getElementById("event-grid-document-row");
  2149.     if (!hasAttachments || !gURL || gURL == "") {
  2150.         documentRow.setAttribute('collapsed', 'true');
  2151.     } else {
  2152.         documentRow.removeAttribute('collapsed');
  2153.         var documentLink = document.getElementById("document-link");
  2154.         var callback = function func() {
  2155.             documentLink.setAttribute('value', gURL);
  2156.         }
  2157.         setTimeout(callback, 1);
  2158.     }
  2159. }
  2160.  
  2161. function browseDocument() {
  2162.     launchBrowser(gURL);
  2163. }
  2164.  
  2165. function updateAttendees() {
  2166.     var regexp = new RegExp("^mailto:(.*)", "i");
  2167.     var attendeeRow = document.getElementById("event-grid-attendee-row");
  2168.     var attendeeRow2 = document.getElementById("event-grid-attendee-row-2");
  2169.     if (!window.attendees || !window.attendees.length) {
  2170.         attendeeRow.setAttribute('collapsed', 'true');
  2171.         attendeeRow2.setAttribute('collapsed', 'true');
  2172.     } else {
  2173.         attendeeRow.removeAttribute('collapsed');
  2174.         attendeeRow2.removeAttribute('collapsed');
  2175.         var attendeeNames = "";
  2176.         var numAttendees = window.attendees.length;
  2177.         for (var i = 0; i < numAttendees; i++) {
  2178.             var attendee = window.attendees[i];
  2179.             if (attendee.commonName && attendee.commonName.length) {
  2180.                 attendeeNames += attendee.commonName;
  2181.             } else if (attendee.id && attendee.id.length) {
  2182.                 var email = attendee.id;
  2183.                 if (regexp.test(email)) {
  2184.                     attendeeNames += RegExp.$1;
  2185.                 } else {
  2186.                     attendeeNames += email;
  2187.                 }
  2188.             } else {
  2189.                 continue;
  2190.             }
  2191.             if (i + 1 < numAttendees) {
  2192.                 attendeeNames += ',';
  2193.             }
  2194.         }
  2195.         var attendeeList = document.getElementById("attendee-list");
  2196.         var callback = function func() {
  2197.             attendeeList.setAttribute('value', attendeeNames);
  2198.         }
  2199.         setTimeout(callback, 1);
  2200.     }
  2201. }
  2202.  
  2203. function updateRepeatDetails() {
  2204.     // Don't try to show the details text for
  2205.     // anything but a custom recurrence rule.
  2206.     var item = window.calendarItem;
  2207.     var recurrenceInfo = window.recurrenceInfo;
  2208.     var itemRepeat = document.getElementById("item-repeat");
  2209.     if (itemRepeat.value == "custom" && recurrenceInfo) {
  2210.         
  2211.         // First of all collapse the details text. If we fail to
  2212.         // create a details string, we simply don't show anything.
  2213.         // this could happen if the repeat rule is something exotic
  2214.         // we don't have any strings prepared for.
  2215.         var repeatDetails = document.getElementById("repeat-details");
  2216.         repeatDetails.setAttribute("collapsed", "true");
  2217.         
  2218.         // Try to create a descriptive string from the rule(s).
  2219.         var kDefaultTimezone = calendarDefaultTimezone();
  2220.         var startDate = jsDateToDateTime(getElementValue("event-starttime"), kDefaultTimezone);
  2221.         var endDate = jsDateToDateTime(getElementValue("event-endtime"), kDefaultTimezone);
  2222.         var allDay = getElementValue("event-all-day", "checked");
  2223.         var detailsString = recurrenceRule2String(
  2224.             recurrenceInfo, startDate, endDate, allDay);
  2225.             
  2226.         // Now display the string...
  2227.         if (detailsString) {
  2228.             var lines = detailsString.split("\n");
  2229.             repeatDetails.removeAttribute("collapsed");
  2230.             while (repeatDetails.childNodes.length > lines.length) {
  2231.                 repeatDetails.removeChild(repeatDetails.lastChild);
  2232.             }
  2233.             var numChilds = repeatDetails.childNodes.length;
  2234.             for (var i = 0; i < lines.length; i++) {
  2235.                 if (i >= numChilds) {
  2236.                     var newNode = repeatDetails.childNodes[0]
  2237.                                                .cloneNode(true);
  2238.                     repeatDetails.appendChild(newNode);
  2239.                 }
  2240.                 repeatDetails.childNodes[i].value = lines[i];
  2241.             }
  2242.         }
  2243.     } else {
  2244.         var repeatDetails = document.getElementById("repeat-details");
  2245.         repeatDetails.setAttribute("collapsed", "true");
  2246.     }
  2247. }
  2248.  
  2249. /**
  2250.  * This function does not strictly check if the given attendee has the status
  2251.  * TENTATIVE, but also if he hasn't responded.
  2252.  *
  2253.  * @param aAttendee     The attendee to check.
  2254.  * @return              True, if the attendee hasn't responded.
  2255.  */
  2256. function isAttendeeUndecided(aAttendee) {
  2257.     return aAttendee.participationStatus != "ACCEPTED" &&
  2258.            aAttendee.participationStatus != "DECLINED" &&
  2259.            aAttendee.participationStatus != "DELEGATED";
  2260. }
  2261.  
  2262. /**
  2263.  * Event handler to set up the attendee-popup. This builds the popup menuitems.
  2264.  *
  2265.  * @param event         The popupshowing event
  2266.  */
  2267. function showAttendeePopup(event) {
  2268.     // Don't do anything for right/middle-clicks
  2269.     if (event.button != 0) {
  2270.         return;
  2271.     }
  2272.  
  2273.     var responsiveAttendees = 0;
  2274.  
  2275.     // anonymous helper function to
  2276.     // initialize a dynamically created menuitem
  2277.     function setup_node(aNode, aAttendee) {
  2278.         // Count attendees that have done something.
  2279.         if (!isAttendeeUndecided(aAttendee)) {
  2280.             responsiveAttendees++;
  2281.         }
  2282.  
  2283.         // Construct the display string from common name and/or email address.
  2284.         var re = new RegExp("^mailto:(.*)", "i");
  2285.         var name = aAttendee.commonName;
  2286.         if (name) {
  2287.             var email = aAttendee.id;
  2288.             if (email && email.length) {
  2289.                 if (re.test(email)) {
  2290.                     name += ' <' + RegExp.$1 + '>';
  2291.                 } else {
  2292.                     name += ' <' + email + '>';
  2293.                 }
  2294.             }
  2295.         } else {
  2296.             var email = aAttendee.id;
  2297.             if (email && email.length) {
  2298.                 if (re.test(email)) {
  2299.                     name = RegExp.$1;
  2300.                 } else {
  2301.                     name = email;
  2302.                 }
  2303.             }
  2304.         }
  2305.         aNode.setAttribute("label", name);
  2306.         aNode.setAttribute("status", aAttendee.participationStatus);
  2307.         aNode.attendee = aAttendee;
  2308.     }
  2309.  
  2310.     // Setup the first menuitem, this one serves as the template for further
  2311.     // menuitems.
  2312.     var attendees = window.attendees;
  2313.     var popup = document.getElementById("attendee-popup");
  2314.     var separator = document.getElementById("attendee-popup-separator");
  2315.     var template = separator.nextSibling;
  2316.  
  2317.     setup_node(template, attendees[0]);
  2318.  
  2319.     // Remove all remaining menu items after the separator and the template menu
  2320.     // item.
  2321.     while (template.nextSibling) {
  2322.         popup.removeChild(template.nextSibling);
  2323.     }
  2324.  
  2325.     // Add the rest of the attendees.
  2326.     for (var i = 1; i < attendees.length; i++) {
  2327.         var attendee = attendees[i];
  2328.         var newNode = template.cloneNode(true);
  2329.         setup_node(newNode, attendee);
  2330.         popup.appendChild(newNode);
  2331.     }
  2332.  
  2333.     // Set up the unanswered attendees item.
  2334.     if (responsiveAttendees == attendees.length) {
  2335.         document.getElementById("cmd_email_undecided")
  2336.                 .setAttribute("disabled", "true");
  2337.     } else {
  2338.         document.getElementById("cmd_email_undecided")
  2339.                 .removeAttribute("disabled");
  2340.     }
  2341.  
  2342.     // Show the popup.
  2343.     var attendeeList = document.getElementById("attendee-list");
  2344.     popup.showPopup(attendeeList, -1, -1, "context", "bottomleft", "topleft");
  2345. }
  2346.  
  2347. /**
  2348.  * Send Email to all attendees that haven't responded or are tentative.
  2349.  *
  2350.  * @param aAttendees    The attendees to check.
  2351.  */
  2352. function sendMailToUndecidedAttendees(aAttendees) {
  2353.     var targetAttendees = attendees.filter(isAttendeeUndecided);
  2354.     sendMailToAttendees(targetAttendees);
  2355. }
  2356.  
  2357. /**
  2358.  * Send Email to all given attendees.
  2359.  *
  2360.  * @param aAttendees    The attendees to send mail to.
  2361.  */
  2362. function sendMailToAttendees(aAttendees) {
  2363.     var toList = "";
  2364.     var item = saveItem();
  2365.  
  2366.     for each (var attendee in aAttendees) {
  2367.         if (attendee.id && attendee.id.length) {
  2368.             var email = attendee.id;
  2369.             var re = new RegExp("^mailto:(.*)", "i");
  2370.             if (email && email.length) {
  2371.                 if (re.test(email)) {
  2372.                     email = RegExp.$1;
  2373.                 } else {
  2374.                     email = email;
  2375.                 }
  2376.             }
  2377.             // Prevent trailing commas.
  2378.             if (toList.length > 0) {
  2379.                 toList += ",";
  2380.             }
  2381.             // Add this recipient id to the list.
  2382.             toList += email;
  2383.         }
  2384.     }
  2385.  
  2386.     // Set up the subject
  2387.     var emailSubject = calGetString("sun-calendar-event-dialog",
  2388.                                     "emailSubjectReply",
  2389.                                     [item.title]);
  2390.  
  2391.     sendMailTo(toList, emailSubject);
  2392. }
  2393.  
  2394. /**
  2395.  * Make sure all fields that may have calendar specific capabilities are updated
  2396.  */
  2397. function updateCapabilities() {
  2398.     updateDocument();
  2399.     updatePriority();
  2400.     updatePrivacy();
  2401. }
  2402.  
  2403. /**
  2404.  * Test if a specific capability is supported
  2405.  *
  2406.  * @param aCap      The capability from "capabilities.<aCap>.supported"
  2407.  */
  2408. function capSupported(aCap) {
  2409.     var calendar = document.getElementById("item-calendar")
  2410.                            .selectedItem.calendar;
  2411.     return calendar.getProperty("capabilities." + aCap + ".supported") !== false;
  2412. }
  2413.  
  2414. /**
  2415.  * Return the values for a certain capability.
  2416.  *
  2417.  * @param aCap      The capability from "capabilities.<aCap>.values"
  2418.  * @return          The values for this capability
  2419.  */
  2420. function capValues(aCap, aDefault) {
  2421.     var calendar = document.getElementById("item-calendar")
  2422.                            .selectedItem.calendar;
  2423.     var vals = calendar.getProperty("capabilities." + aCap + ".values");
  2424.     return (vals === null ? aDefault : vals);
  2425. }
  2426.